/**
 * GeoSOA/Spatialytics/SOLAPContext.js
 *
 * Main class encapsulating needed data and context for a SOLAP layer (using
 * OpenLayers' vector layers)
 *
 * Copyright (C) 2008-2009 GeoSOA Research Group, Universite Laval
 * Copyright (C) 2010 AtolCD
 * See LICENSE file under this distribution for licensing information
 * 
 * @author Etienne Dube
 * @since 200807
 * @author Vincent Plantegenet
 * @since 20100324
 **/

dojo.provide("GeoSOA.Spatialytics.SOLAPContext");

dojo.require("GeoSOA.Spatialytics.mapstyles.MapStyle");
dojo.require("GeoSOA.Spatialytics.Featurizer");

dojo.require("GeoSOA.olap4js.CellSet");
dojo.require("GeoSOA.util.ArrayList");

dojo.require("dojo.parser");
dojo.require("dijit.Toolbar");
dojo.require("dijit.Dialog");
dojo.require("dijit.form.Button");

/**
 * Holds the context and other data needed for rendering and displaying
 * Spatial OLAP data (provided by an OlapAjax object) on an OpenLayers map.
 * 
 * @param {Object} options Options passed to the constructor
 * @constructor
 */
GeoSOA.Spatialytics.SOLAPContext = function (options) {

	var options = options || {};
	
	/**
	 * {GeoSOA.olap4js.OlapAjax} options.olapAjax (required) OlapAjax object
	 * 
	 * @public
	 */
	this.olapAjax = options.olapAjax;
	
	/**
	 * {DOMNode} options.solapToolbarDOMNode (required) Node of the div
	 * element used to hold the Dojo toolbar (with OLAP navops buttons).
	 * A toolbar will be instanciated in this div.
	 */
	// TODO: make it a dijit widget
	this._solapToolbarDOMNode = options.solapToolbarDOMNode;
	
	/**
	 * {Object} options.mapStyle (optional)
	 * GeoSOA.Spatialytics.mapstyles.MapStyle object responsible for styling
	 * the SOLAP layer, and containing a suitable Featurizer object for this style.
	 * If omitted, a default MapStyle is constructed (by calling its constructor
	 * without options).
	 * Callers SHOULD specify a MapStyle for their own maps.
	 */
	
	this.mapStyle = options.mapStyle || new GeoSOA.Spatialytics.mapstyles.MapStyle();
	
	//reference to the map 
	this.mapRef = options.mapRef;
	
	this.reqDone = false;
	this.vlayer = vlayer;
	/**
	 * {Object} options.styleMap (optional) OpenLayers.StyleMap object
	 * responsible for styling the SOLAP layer. If omitted, a default
	 * StyleMap is constructed from a fixed intervals MapStyle object.
	 * 
	 */
	// var styleMap = options.styleMap ||
	//	(new GeoSOA.Spatialytics.mapstyles.MapStyle()).getDefaultStyleMap();
	
	/**
	 * {Object} options.projection (optional) OpenLayers.Projection specifying
	 * the Spatial Reference System (SRS) to use for the SOLAP layer. Default
	 * is EPSG 4326 (WGS84 lat/long geographic).
	 */
	this.projection = options.projection || new OpenLayers.Projection("EPSG:2154");
	
	/**
	 * {String} options.jpurl pecifying
	 * the JPivot application URL for displaying in Tab 
	 */
	this.jpivotURL = options.jpurl;
	
	//All the params to be passed for access by filter tree
	this.config = options.conf;
	this.config2 = options.conf2;
	this.curr_cube = 0;
	var that = this;
	
	/**
	 * Array of OpenLayers.Feature.Vector containing the features
	 * to be drawn on the SOLAP layer. Note: private; use the
	 * getVectors() / setVectors() method to access. 
	 * @private
	 */
	this._vectors = [];
	
	/**
	 * The vector layer object for SOLAP features
	 * @public
	 */ 
	this.vectorLayer = new OpenLayers.Layer.Vector("Spatialytics test",
		{
			// format: OpenLayers.Format.GeoJSON,
			features: that._vectors,
			styleMap: that.mapStyle.getStyleMap(),
			// isBaseLayer: true,
			projection: that.projection,
			// default renderers:
			// note: Canvas' performance is poor when highlighting features
			// (needs to redraw all features in the layer)
			//renderers: ['Canvas', 'SVG', 'VML']
		} );
	
	/**
	 * Observable object for the "after layer refresh" event
	 * 
	 * This event occurs when getting new data in OlapAjax (and once the
	 * _vectors and vectorLayer objects are updated, but before calling
	 * redraw() on the layer). These observer functions will be passed
	 * this SOLAPContext object as an argument, and are to be called from
	 * the _refreshSOLAPLayer() method.
	 * @public
	 */
	this.afterLayerRefreshObservable = new GeoSOA.util.Observable(this);
	
	
	/**
	 * Observable object for displaying detailed informations on selected feature
	 * 
	 */
	this.onZoneChangeObservable = new GeoSOA.util.Observable(this);
	
	/**
	 * Toolbar for OLAP navigation operators:
	 * @private
	 */
	this._solapToolbar =
		new GeoSOA.Spatialytics.SOLAPContext.SOLAPToolbar(this._solapToolbarDOMNode,this);

	// registers the SOLAPContext observer function to the OlapAjax object
	this.olapAjax.afterResponseObservable.register(this.getOlapAjaxObserver());
	
	//A placer ailleurs
	var default_Hierarchy = null;
	var current_Hierarchy = null;

};


/**
 * Get the GeoJSON object retrieved by the last submitted query. Used to draw
 * the map layer in OpenLayers. 
 * 
 * @return {Object} GeoJSON object
 * @public
 */
GeoSOA.Spatialytics.SOLAPContext.prototype.getGeoJSON = function () {
	return this.getMapStyle().featurizer.featurize(this.olapAjax.getCellSet());
}


/**
 * Set the SOLAP layer's vector features and update the this.vectorLayer
 * accordingly
 * 
 * @param {Array} vectors Array of OpenLayers.Feature.Vector
 * @public
 */
GeoSOA.Spatialytics.SOLAPContext.prototype.setVectors = function (vectors) {
	this._vectors = vectors;
	this.vectorLayer.destroyFeatures();
	this.vectorLayer.addFeatures(this._vectors);
};


/**
 * Get the SOLAP layer's vector features
 * 
 * @return {Array} Array of OpenLayers.Feature.Vector
 * @public
 */
GeoSOA.Spatialytics.SOLAPContext.prototype.getVectors = function () {
	return this._vectors;
};


/**
 * Set the MapStyle object
 * 
 * @param {Object} mapStyle GeoSOA.Spatialytics.mapstyles.MapStyle object
 * @public
 */
GeoSOA.Spatialytics.SOLAPContext.prototype.setMapStyle = function (mapStyle) {
	this.mapStyle = mapStyle;
	this.vectorLayer.styleMap = this.mapStyle.getStyleMap();
	this._refreshSOLAPLayer();
};


/**
 * Get the current MapStyle object
 * 
 * @return {Object} GeoSOA.Spatialytics.mapstyles.MapStyle object
 * @public
 */
GeoSOA.Spatialytics.SOLAPContext.prototype.getMapStyle = function () {
	return this.mapStyle;
};


/**
 * Set the OpenLayers.StyleMap for the vector layer
 * 
 * @param {Object} styleMap OpenLayers.StyleMap object
 * @public
 * @deprecated
 */
GeoSOA.Spatialytics.SOLAPContext.prototype.setStyleMap = function (styleMap) {
	this.vectorLayer.styleMap = styleMap;
};

/**
 * Redraw the SOLAP vector layer. Call this after a change to any
 * styling properties.
 * 
 * @public
 */
GeoSOA.Spatialytics.SOLAPContext.prototype.redrawLayer = function () {
	this.vectorLayer.redraw();

};


/**
 * Returns an observer function suitable for registering with an
 * OlapAjax object
 * 
 * @return Observer closure function
 * @type Function
 */
GeoSOA.Spatialytics.SOLAPContext.prototype.getOlapAjaxObserver = function () {
	var that = this;
	
	var closure = function (olapAjax) {
		//updates JPivot frame
		var frame = document.getElementById("jpivotframe");
		var qr = olapAjax.getMdxQuery().toString();

		qr = qr.replace(/''''/g,"''");
		qr = encodeURI(qr.toString());
		url = that.jpivotURL+"&mdxQuery="+qr.toString(); 	
		frame.src=url;
		
		//updates the SOLAP layer
		that._refreshSOLAPLayer();
	};
	
	return closure;
};

GeoSOA.Spatialytics.SOLAPContext.prototype.addCircles = function (vec) {
	var poly = [];
	for(var i in vec){	
		var pt = vec[i].geometry.getCentroid();
		var tmp_poly = OpenLayers.Geometry.Polygon.createRegularPolygon(pt, (vec[i].data.value/1000000), 20);
		poly[i] = new OpenLayers.Feature.Vector(tmp_poly);
	}
	this.vlayer.addFeatures(poly);
}
/**
 * Refreshes the SOLAP layer (called by the response handler) 
 * 
 */
GeoSOA.Spatialytics.SOLAPContext.prototype._refreshSOLAPLayer = function () {

	var i;
	
	var geojson_format = new OpenLayers.Format.GeoJSON();
	
	// replace the vectors in the layer by the ones parsed from GeoJSON
	// this.setVectors(geojson_format.read(geoJSONstr));
	
	var newVect = geojson_format.read(this.getGeoJSON());
	
	//Option to draw circles
	//this.addCircles(newVect);
	//this.vlayer.addfeatures(this.addCircles(newVect));
	
	
	this.setVectors(newVect);
	
	// call any observer functions that need to be invoked after refreshing
	// the vector features (e.g. to get min/max value bounds and update
	// a dynamic equal intervals MapStyle))
	// ("after layer refresh" event)
	this.afterLayerRefreshObservable.update();
	
	// redraw the layer with the new vectors
	this.redrawLayer();
};


/**
 * Returns a closure for handling feature selection in OpenLayers
 * 
 * @return {Function} closure function
 * @private
 */
GeoSOA.Spatialytics.SOLAPContext.prototype._getFeatureSelectionClosure = function () {
	
	var that = this;
	
	var closure = function () {
		
		//modifs
		var originalCellSet = that.olapAjax.getCellSet();
	
		//fin modifs
		var selFeat = that.vectorLayer.selectedFeatures[0];
		var memberName = selFeat.attributes["memberName"];
		var memberVal = selFeat.attributes["value"];
		var axisOrd = selFeat.attributes["axisOrdinal"];
		var posOrd = selFeat.attributes["positionOrdinal"];
		var memOrd = selFeat.attributes["memberOrdinal"];

		that.onZoneChangeObservable.update();
	};
	
	return closure;
};


/**
 * Returns an OpenLayers control for selection of features on the SOLAP
 * vector layer of this SOLAPContext (to enable OLAP navigation). The
 * resulting control must be added to the OpenLayers map and activated.
 * @param Boolean multi
 * @return {OpenLayers.Control.SelectFeature} OpenLayers SelectFeature control
 * object
 */
GeoSOA.Spatialytics.SOLAPContext.prototype.getOpenLayersSelectControl = function (select) {
	var that = this;
	
	// instantiates a SelectFeature control acting on the current
	// SOLAP vector layer, and calling an handler method on feature
	// selection
	var controlSel = new OpenLayers.Control.SelectFeature(
			that.vectorLayer,
			{
				multiple: true,
				hover: false,
				toggle: true,
				onSelect: that._getFeatureSelectionClosure(),
				id:'selFeat'
			}
	);
	
	var control = new OpenLayers.Control.SelectFeature(
			that.vectorLayer,
			{
				multiple: false,
				hover: false,
				toggle: false,
				onSelect: that._getOpenLayersClickHandlerAsClosure(),
				id:'clickFeat'
			}
	);
	
	var detailsCtrl = new OpenLayers.Control.SelectFeature(
			that.vectorLayer,
			{
				multiple: false,
				hover: true,
				toggle: false,
				onSelect: that._getFeatureSelectionClosure(),
				id:'detailsCtrl'
			}
	);
	
	if(select==1){
		return controlSel;
	}
	else if(select==0){
		return control;
	}
	else{
		return detailsCtrl;
	}
		
};


/**
 * Class used for the OLAP navops toolbar (using Dojo toolbars)
 * 
 * @param {DOMNode} toolbarDOMNode (required) node of the div to contain
 * the Dojo toolbar
 * @constructor
 */
// TODO: make it a dijit widget
GeoSOA.Spatialytics.SOLAPContext.SOLAPToolbar = function (toolbarDOMNode,solapC) {

	this._toolbarDOMNode = toolbarDOMNode;
	this._toolbar = new dijit.Toolbar(null, this._toolbarDOMNode);
	this._activeTool = null;
	var that = solapC;
	// toolbar buttons:
	this._toolbarButtons = {
			drillReplace: new dijit.form.ToggleButton(
					{
						iconClass: "solapMapIcon solapMapIconDrillReplace",
						showLabel: false,
						label: "Drill replace"
					}

			),
			rollUpLevel: new dijit.form.ToggleButton(
					{
						iconClass: "solapMapIcon solapMapIconRollUp",
						showLabel: false,
						label: "Roll-up by level"
					}
			),
			
			geoSelection: new dijit.form.ToggleButton(
					{
						iconClass: "solapMapIcon solapMapIconGeom",
						showLabel: false,
						label: "Geometry Selection"
					}
			),
			
			details: new dijit.form.ToggleButton(
					{
						iconClass: "solapMapIcon solapMapIconDetails",
						showLabel: false,
						label: "Show Details"
					}
			)
	};
	
	// add all the defined buttons to the toolbar,
	// and connect them to the event handler
	for (var prop in this._toolbarButtons) {
		this._toolbar.addChild(this._toolbarButtons[prop]);//ajout du bouton
		var eventHandlerObj = new this._ToolbarButtonEventHandler(this, prop, that);
		dojo.connect(this._toolbarButtons[prop], "onClick", eventHandlerObj,
				"handleEvent");
	}
	
	// set the default tool:
	this.setActiveTool("details");
};


/**
 * Sets the active OLAP navop tools, thereby deactivating any other active
 * tool.
 * 
 * @param {String} toolName (required) name of the OLAP navop to activate
 * @public
 */
GeoSOA.Spatialytics.SOLAPContext.SOLAPToolbar.prototype.setActiveTool =
	function (toolName) {
	var tool = this._toolbarButtons[toolName];
	if (tool !== undefined) {
		for (var prop in this._toolbarButtons) {
			this._toolbarButtons[prop].attr('checked', false);
		}
		this._toolbarButtons[toolName].attr('checked', true);
		this._activeTool = toolName;
	}
};


/**
 * Returns the name of the current active OLAP navop tool.
 * 
 * @return {String} name of the active tool
 * @public
 */
GeoSOA.Spatialytics.SOLAPContext.SOLAPToolbar.prototype.getActiveTool = function () {
	return this._activeTool;
};


/**
 * Class used to hold event handlers for OLAP navops toolbar buttons.
 * Instances are initialized with the current GeoSOA.Spatialytics.SOLAPToolbar
 * object and the OLAP navop name (toolName), and their handleEvent()
 * method is called by the Dojo toolkit on the onClick event.
 * See the GeoSOA.Spatialytics.SOLAPToolbar constructor for usage.
 * 
 * @param {Object} toolbarContext
 * @param {String} toolName
 * @constructor
 * @private
 */
GeoSOA.Spatialytics.SOLAPContext.SOLAPToolbar.prototype._ToolbarButtonEventHandler =
	function (toolbarContext, toolName, solapC) {
	this._toolbarContext = toolbarContext;
	this._toolName = toolName;
	this._solapC = solapC;
};


/**
 * Event handler called by the Dojo toolkit on the onClick event of
 * toolbar buttons, responsible for setting the active tool in a
 * GeoSOA.Spatialytics.SOLAPToolbar instance.
 * 
 * @param {Object} event (unused)
 * @public
 */
GeoSOA.Spatialytics.SOLAPContext.SOLAPToolbar.prototype._ToolbarButtonEventHandler.prototype.handleEvent =
	function (event) {

	if(this._toolName.toString()=="drillReplace"||this._toolName.toString()=="rollUpLevel"){
		this._solapC.mapRef.getControl("selFeat").deactivate();
		this._solapC.mapRef.getControl("detailsCtrl").deactivate();
		this._solapC.mapRef.getControl("clickFeat").activate();
	}
	else if(this._toolName.toString()=="geoSelection"){
		this._solapC.mapRef.getControl("clickFeat").deactivate();
		this._solapC.mapRef.getControl("detailsCtrl").deactivate();
		this._solapC.mapRef.getControl("selFeat").activate();
	}
	else if(this._toolName.toString()=="details"){
		this._solapC.mapRef.getControl("selFeat").deactivate();
		this._solapC.mapRef.getControl("clickFeat").deactivate();
		this._solapC.mapRef.getControl("detailsCtrl").activate();
	}
	
	this._toolbarContext.setActiveTool(this._toolName);
};


/**
 * Control for handling clicks in OpenLayers (used to handle OLAP navops when
 * clicking on map features)
 * 
 * @param {Object} options Options object; set the onClickHandler property
 * to pass a closure function for handling clicks on features.
 * 
 * @constructor
 * @extends OpenLayers.Control 
 * @private
 */
GeoSOA.Spatialytics.SOLAPContext._OpenLayersClickControlClass =
	OpenLayers.Class(OpenLayers.Control, {                
    onClickHandler: null,
	
	defaultHandlerOptions: {
        'single': true, 
        'double': false,
        'pixelTolerance': 0,
        'stopSingle': false,
        'stopDouble': false
    },

    initialize: function (options) {
    	OpenLayers.Control.prototype.initialize.apply(this, [options]);
        this.handlerOptions = OpenLayers.Util.extend(
            {}, this.defaultHandlerOptions);
        this.handler = new OpenLayers.Handler.Click(
            this, {
                'click': this.onClick,
                'dblclick': this.onDblclick 
            }, this.handlerOptions);
    }, 

    // handler function:
    onClick: function (evt) {
		this.onClickHandler();
    },

    onDblclick: function (evt) {  

    }

});


/**
 * Instantiates a GeoSOA.Spatialytics.SOLAPContext._OpenLayersClickControlClass
 * object, passing to the ctor a closure containing the current SOLAPContext
 * object used to handle OLAP navops. The resulting control must be added to
 * the OpenLayers map and activated.
 * 
 * @return {GeoSOA.Spatialytics.SOLAPContext._OpenLayersClickControlClass}
 * OpenLayers control
 * @public
 */
GeoSOA.Spatialytics.SOLAPContext.prototype.getOpenLayersClickControl = function () {
	var that = this;
	var clickControl =
		new GeoSOA.Spatialytics.SOLAPContext._OpenLayersClickControlClass(
				{ onClickHandler:
					that._getOpenLayersClickHandlerAsClosure() }
		);
	return clickControl;
};


/**
 * Returns a closure containing the current GeoSOA.Spatialytics.SOLAPContext
 * object, to be used in an OpenLayers click control which handles clicks on
 * features of a SOLAP layer (for handling OLAP navops such as drill-down).
 * 
 * @return {Function} closure function
 * @private
 */
GeoSOA.Spatialytics.SOLAPContext.prototype._getOpenLayersClickHandlerAsClosure =
	function () {
	var that = this;

	var closureFunc = function () {
		// any selected features?
		if (that.vectorLayer.selectedFeatures !== undefined &&
				that.vectorLayer.selectedFeatures.length > 0) {
				
			if(!that.reqDone){
			
			var Wdlg = new dijit.Dialog({
						title: "Error",
						style: "width: 300px",
						draggable: false
					});
			Wdlg.attr("content", "Make a request first!");
			Wdlg.show();
			return;
			}
				
			var selFeat = that.vectorLayer.selectedFeatures[0];
			var axisOrd = selFeat.attributes["axisOrdinal"];
			var posOrd = selFeat.attributes["positionOrdinal"];
			var memOrd = selFeat.attributes["memberOrdinal"];

			
			if(that._solapToolbar.getActiveTool().toString()=="drillReplace"){
				if(that.vectorLayer.selectedFeatures[0].children==0){
					Wdlg.attr("content", "No more children");
					Wdlg.show();
				}else{
					var queryObj = that.olapAjax.getMdxQueryObj();
					var query = queryObj.olapNav(that.vectorLayer.selectedFeatures[0].data.uniqueName,0);
					
					that.olapAjax.setMdxQueries(query,queryObj);
					that.olapAjax.submitQuery();
				}
			}
			else if(that._solapToolbar.getActiveTool().toString()=="rollUpLevel"){
				if(that.vectorLayer.selectedFeatures[0].parent==0){
					//TODO throw something
				}else{
					var queryObj = that.olapAjax.getMdxQueryObj();
					var query = queryObj.olapNav(that.vectorLayer.selectedFeatures[0].data.uniqueName,1);
				
					that.olapAjax.setMdxQueries(query,queryObj);
					that.olapAjax.submitQuery();
				}
			}
		}
	};
	return closureFunc;
};